package org.cainiao.basic.invoke;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

/**
 * <br />
 * <p>
 * Author: Cai Niao(wdhlzd@163.com)<br />
 */
public class InvokeTest {

    public static void main(String[] args) throws Throwable {
        String s;
        MethodType mt;
        MethodHandle mh;
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        /*
         * mt is (char,char)String
         * methodType() 的第一个参数为方法描述符的返回值类型
         * 之后的参数为方法描述符的参数类型
         */
        mt = MethodType.methodType(String.class, char.class, char.class);
        /*
         * findVirtual()
         * 从 String.class 类型中找到符合 MethodType 为 (char,char)String 的成员方法
         */
        mh = lookup.findVirtual(String.class, "replace", mt);
        /*
         * 调用成员方法，调用时第一个参数为对象引用，这里为 "daddy" 字符串对象
         * 等价于执行字节码指令 invokevirtual
         * 指令操作数为：符号引用为 (char,char)String
         * 操作数栈自上而下的值分别是："daddy" 字符串对象的引用、'd'、'n'
         */
        s = (String) mh.invokeExact("daddy", 'd', 'n');
        // invokeExact(Ljava/lang/String;CC)Ljava/lang/String;
        assertEquals(s, "nanny");
        // weakly typed invocation (using MHs.invoke)
        s = (String) mh.invokeWithArguments("sappy", 'p', 'v');
        assertEquals(s, "savvy");
        // mt is (Object[])List
        mt = MethodType.methodType(java.util.List.class, Object[].class);
        mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
        assert (mh.isVarargsCollector());
        Object x;
        x = mh.invoke("one", "two");
        // invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
        assertEquals(x, java.util.Arrays.asList("one", "two"));
        // mt is (Object,Object,Object)Object
        mt = MethodType.genericMethodType(3);
        mh = mh.asType(mt);
        x = mh.invokeExact((Object) 1, (Object) 2, (Object) 3);
        // invokeExact(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
        assertEquals(x, java.util.Arrays.asList(1, 2, 3));
        // mt is ()int
        mt = MethodType.methodType(int.class);
        mh = lookup.findVirtual(java.util.List.class, "size", mt);
        int i;
        i = (int) mh.invokeExact(java.util.Arrays.asList(1, 2, 3));
        // invokeExact(Ljava/util/List;)I
        assert (i == 3);
        mt = MethodType.methodType(void.class, String.class);
        mh = lookup.findVirtual(java.io.PrintStream.class, "println", mt);
        mh.invokeExact(System.out, "Hello, world.");
        // invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V
    }

    private static void assertEquals(Object o1, Object o2) {
        if (!o1.equals(o2)) {
            throw new RuntimeException("not equals");
        }
    }
}
